package cn.geminis.crypto.csp;

import cn.geminis.core.exception.CommonException;
import cn.geminis.crypto.core.util.ObjectUtils;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.operator.DefaultDigestAlgorithmIdentifierFinder;
import org.bouncycastle.operator.DefaultSignatureAlgorithmIdentifierFinder;
import org.bouncycastle.tsp.TSPUtil;

import java.io.Closeable;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

/**
 * @author Allen
 */
public abstract class AbstractCspFactory implements Closeable {

    private static final HashMap<String, AbstractCspFactory> SIGNER_FACTORY = new HashMap<>();
    private static final HashMap<String, AbstractCspFactory> DIGEST_FACTORY = new HashMap<>();
    private static final HashMap<String, AbstractCspFactory> BLOCK_CIPHER_FACTORY = new HashMap<>();
    private static final HashMap<String, AbstractCspFactory> KEY_PAIR_GENERATOR_FACTORY = new HashMap<>();

    public static String findDigestAlgName(String algOid) {
        AbstractCspFactory factory = DIGEST_FACTORY.get(algOid);
        if (Objects.isNull(factory)) {
            throw new CommonException("未注册的算法：" + algOid);
        }
        return factory.getDigestAlgName();
    }

    public static String findSignerAlgName(String algOid) {
        AbstractCspFactory factory = SIGNER_FACTORY.get(algOid);
        if (Objects.isNull(factory)) {
            throw new CommonException("未注册的算法：" + algOid);
        }
        return factory.getSignerAlgName();
    }

    public static AbstractSigner getSigner(String oid) {
        AbstractCspFactory factory = SIGNER_FACTORY.get(oid);
        if (factory == null) {
            throw new CommonException("算法为" + oid + "的签名器没有注册");
        }
        return factory.createSigner();
    }

    public static AbstractDigest getDigest(String oid) {
        AbstractCspFactory factory = DIGEST_FACTORY.get(oid);
        if (factory == null) {
            throw new CommonException("算法为" + oid + "的数据摘要器没有注册");
        }
        return factory.createDigest();
    }

    public static AbstractBlockCipher getBlockCipher(String oid) {
        AbstractCspFactory factory = BLOCK_CIPHER_FACTORY.get(oid);
        if (factory == null) {
            throw new CommonException("算法为" + oid + "的对称加密器没有注册");
        }
        return factory.createBlockCipher();
    }

    public static KeyPairGenerator getKeyPairGenerator(String oid) {
        AbstractCspFactory factory = KEY_PAIR_GENERATOR_FACTORY.get(oid);
        if (factory == null) {
            throw new CommonException("算法为" + oid + "的密钥对产生器没有注册");
        }
        return factory.createKeyPairGenerator();
    }

    public static void clear() {
        SIGNER_FACTORY.forEach((key, value) -> {
            try {
                value.close();
            } catch (Exception e) {
                throw new CommonException("释放CspFactory错误", e);
            }
        });

        SIGNER_FACTORY.clear();
        DIGEST_FACTORY.clear();
        BLOCK_CIPHER_FACTORY.clear();
        KEY_PAIR_GENERATOR_FACTORY.clear();
    }

    private void registerMap(Class<?> clazz, String field, Object key, Object value) {
        var map = (Map<Object, Object>) ObjectUtils.getValue(clazz, field);
        map.computeIfAbsent(key, k -> value);
    }

    protected void register() {
        SIGNER_FACTORY.put(this.getSignerAlgOid(), this);
        SIGNER_FACTORY.put(this.getKeyPairOid(), this);
        DIGEST_FACTORY.put(this.getDigestAlgOid(), this);
        BLOCK_CIPHER_FACTORY.put(this.getBlockCipherAlgOid(), this);
        KEY_PAIR_GENERATOR_FACTORY.put(this.getKeyPairOid(), this);

        ASN1ObjectIdentifier signerOid = new ASN1ObjectIdentifier(getSignerAlgOid());
        ASN1ObjectIdentifier digestOid = new ASN1ObjectIdentifier(getDigestAlgOid());

        registerMap(DefaultDigestAlgorithmIdentifierFinder.class,
                "digestOids",
                signerOid,
                digestOid
        );

        try (AbstractDigest digest = this.createDigest()) {
            registerMap(TSPUtil.class,
                    "digestLengths",
                    getDigestAlgOid(),
                    digest.getDigestSize()
            );
        } catch (IOException e) {
            throw new CommonException("创建摘要器失败", e);
        }

        String signOid = getDigestAlgOid() + "WITH" + getSignerAlgOid();
        registerMap(DefaultSignatureAlgorithmIdentifierFinder.class,
                "algorithms",
                signOid,
                signerOid
        );
    }

    /**
     * 产生随机数产生器
     *
     * @return 随机数产生器
     */
    public abstract RandomGenerator createRandomGenerator();

    /**
     * 获取签名算法OID
     *
     * @return 签名算法OID
     */
    public abstract String getSignerAlgOid();

    /**
     * 获取非对称加密算法OID
     *
     * @return 签名算法OID
     */
    public abstract String getAsyncEncryptionAlgOid();

    /**
     * 获取签名算法名称
     *
     * @return 签名算法名称
     */
    public abstract String getSignerAlgName();

    /**
     * 获取摘要算法OID
     *
     * @return 摘要算法OID
     */
    public abstract String getDigestAlgOid();

    /**
     * 获取摘要算法名称
     *
     * @return 摘要算法名称
     */
    public abstract String getDigestAlgName();

    /**
     * 获取对称加密算法OID
     *
     * @return 对称加密算法OID
     */
    public abstract String getBlockCipherAlgOid();

    /**
     * 获取密钥OID
     *
     * @return 密钥OID
     */
    public abstract String getKeyPairOid();

    /**
     * 获取摘要计算器
     *
     * @return 摘要计算器
     */
    public abstract AbstractDigest createDigest();

    /**
     * 获取签名器
     *
     * @return 签名器
     */
    public abstract AbstractSigner createSigner();

    /**
     * 获取对称加密器
     *
     * @return 对称加密器
     */
    public abstract AbstractBlockCipher createBlockCipher();

    /**
     * 获取对称加密器
     *
     * @return 对称加密器
     */
    public abstract AbstractAsymmetricBlockCipher createAsymmetricBlockCipher();

    /**
     * 获取密钥产生器
     *
     * @return 密钥产生器
     */
    public abstract KeyPairGenerator createKeyPairGenerator();

    /**
     * 获取密钥协商器
     *
     * @return 密钥协商器
     */
    public abstract AbstractAgreement createAgreement();

    /**
     * 获取MAC计算器
     *
     * @return MAC计算器
     */
    public abstract AbstractMac createMac();

    public Envelope createEnvelope() {
        return new Envelope(createBlockCipher(), createAsymmetricBlockCipher(), createRandomGenerator());
    }
}
